package com.rybbaby.tss.core.web.converter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.util.StreamUtils;

/**
 * @description 重写org.springframework.http.converter.StringHttpMessageConverter
 *              类，修改默认编码为UTF-8,防止异步请求时出现乱码
 * 
 * @author 谢进伟
 * 
 * @createDate 2016-1-30 上午11:10:15
 */
public class UTF8StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
	
	public static final Charset DEFAULT_CHARSET = Charset.forName("UTf-8");
	
	private final Charset defaultCharset;
	
	private final List<Charset> availableCharsets;
	
	private boolean writeAcceptCharset = true;
	
	/**
	 * A default constructor that uses {@code "ISO-8859-1"} as the default
	 * charset.
	 * 
	 * @see #StringHttpMessageConverter(Charset)
	 */
	public UTF8StringHttpMessageConverter (){
		this(DEFAULT_CHARSET);
	}
	
	/**
	 * A constructor accepting a default charset to use if the requested content
	 * type does not specify one.
	 */
	public UTF8StringHttpMessageConverter (Charset defaultCharset ){
		super(new MediaType("text" , "plain" , defaultCharset) , MediaType.ALL);
		this.defaultCharset = defaultCharset;
		this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
	}
	
	/**
	 * Indicates whether the {@code Accept-Charset} should be written to any
	 * outgoing request.
	 * <p>
	 * Default is {@code true}.
	 */
	public void setWriteAcceptCharset(boolean writeAcceptCharset) {
		this.writeAcceptCharset = writeAcceptCharset;
	}
	
	@Override
	public boolean supports(Class<?> clazz) {
		return String.class == clazz;
	}
	
	@Override
	protected String readInternal(Class<? extends String> clazz , HttpInputMessage inputMessage) throws IOException {
		Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
		return StreamUtils.copyToString(inputMessage.getBody() , charset);
	}
	
	@Override
	protected Long getContentLength(String str , MediaType contentType) {
		Charset charset = getContentTypeCharset(contentType);
		try {
			return (long)str.getBytes(charset.name()).length;
		} catch (UnsupportedEncodingException ex) {
			// should not occur
			throw new IllegalStateException(ex);
		}
	}
	
	@Override
	protected void writeInternal(String str , HttpOutputMessage outputMessage) throws IOException {
		if(this.writeAcceptCharset) {
			outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
		}
		Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
		StreamUtils.copy(str , charset , outputMessage.getBody());
	}
	
	/**
	 * Return the list of supported {@link Charset}s.
	 * <p>
	 * By default, returns {@link Charset#availableCharsets()}. Can be
	 * overridden in subclasses.
	 * 
	 * @return the list of accepted charsets
	 */
	protected List<Charset> getAcceptedCharsets() {
		return this.availableCharsets;
	}
	
	private Charset getContentTypeCharset(MediaType contentType) {
		if(contentType != null && contentType.getCharSet() != null) {
			return contentType.getCharSet();
		} else {
			return this.defaultCharset;
		}
	}
	
}
